/*
 * abstractsyntaxtree.cpp
 *
 *  Created on: 15.04.2011
 *      Author: chrschn
 */

#include <antlr3.h>
#include <C.h>
#include <abstractsyntaxtree.h>
#include <astscopemanager.h>
#include <debug.h>

AbstractSyntaxTree::AbstractSyntaxTree()
    : _scopeMgr(0), _rootNodes(0), _input(0), _lxr(0), _tstream(0), _psr(0)
{
    _scopeMgr = new ASTScopeManager(this);
}


AbstractSyntaxTree::~AbstractSyntaxTree()
{
    clear();
    if (_scopeMgr)
        delete _scopeMgr;
}


void AbstractSyntaxTree::clear()
{
    for (ASTNodeQList::iterator it = _nodes.begin();
            it != _nodes.end(); ++it)
    {
        pASTNode p = *it;
        delete p;
    }
    _nodes.clear();

    for (ASTNodeListQList::iterator it = _nodeLists.begin();
            it != _nodeLists.end(); ++it)
    {
        pASTNodeList p = *it;
        delete p;
    }
    _nodeLists.clear();

    for (ASTTokenListQList::iterator it = _tokenLists.begin();
            it != _tokenLists.end(); ++it)
    {
        pASTTokenList p = *it;
        delete p;
    }
    _tokenLists.clear();

    _scopeMgr->clear();
    _rootNodes = 0;

    if (_psr) {
        _psr->free(_psr);
        _psr = 0;
    }

    if (_tstream) {
        _tstream ->free(_tstream);
        _tstream = 0;
    }

    if (_lxr) {
        _lxr->free(_lxr);
        _lxr = 0;
    }

    if (_input) {
        _input->close(_input);
        _input = 0;
    }
}


int AbstractSyntaxTree::parse(const QByteArray& asciiText, ASTBuilder* builder)
{
    clear();

    _fileName.clear();
    _input = antlr3NewAsciiStringCopyStream(
    		(pANTLR3_UINT8)asciiText.constData(),
    		asciiText.size(),
    		0);

    return parsePhase2(builder);
}


int AbstractSyntaxTree::parse(const QString& fileName, ASTBuilder* builder)
{
    clear();

    _fileName = fileName;
    QByteArray s = fileName.toAscii();
    _input = antlr3AsciiFileStreamNew((pANTLR3_UINT8) s.constData());

    // The input will be created successfully, providing that there is enough
    // memory and the file exists etc
    //
    if (!_input) {
        debugerr("Failed to open file " << fileName);
        return ANTLR3_ERR_NOFILE;
    }

    return parsePhase2(builder);
}


int AbstractSyntaxTree::parsePhase2(ASTBuilder* builder)
{
	assert(_input);

    // CLexerNew is generated by ANTLR
    _lxr = CLexerNew(_input);

    if (!_lxr) {
        debugerr("Unable to create the lexer due to malloc() failure");
        return ANTLR3_ERR_NOMEM;
    }

    _tstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT,
            _lxr->pLexer->tokSource);

    if (!_tstream) {
        debugerr("Out of memory trying to allocate token stream");
        return ANTLR3_ERR_NOMEM;
    }

    // CParserNew is generated by ANTLR3
    _psr = CParserNew(_tstream);

    if (!_psr) {
        debugerr("Out of memory trying to allocate parser\n");
        return ANTLR3_ERR_NOMEM;
    }

    // Install our own error display function
    _psr->pParser->rec->displayRecognitionError = displayParserRecognitionError;

    // Start to parse the source
    _rootNodes = _psr->translation_unit(_psr, builder);

    // See if we could parse the source up to the last token
    if (_tstream->p > 0 && _tstream->p == (ANTLR3_INT64)_tstream->tokens->count)
    {
        // In case the parser recoverd from errors, we return -1, otherwise 0
        return errorCount() ? -1 : 0;

//        pANTLR3_COMMON_TOKEN tok = (pANTLR3_COMMON_TOKEN)_tstream->tokens->get(_tstream->tokens, _tstream->p);
//        if (!tok)
//            debugerr("Null token");
//        else
//            debugmsg("We stopped at line " << tok->line
//                    << " (token " << _tstream->p << " of "
//                    << _tstream->tokens->count << ")");
    }

    return errorCount();
}


void AbstractSyntaxTree::printScopeRek(ASTScope* sc)
{
	// Print current scope
	for (ASTScope* scope = sc ? sc : _scopeMgr->currentScope(); scope;
			scope = scope->parent())
	{
		debugmsg(
				QString("================================="
						"[Scope 0x%1]"
						"=================================")
						.arg((quint64)scope, 0, 16));

		QMultiHash<quint64, const ASTSymbol*> symByLine;
		for (ASTSymbolHash::iterator it = scope->symbols().begin();
				it != scope->symbols().end(); ++it)
		{
			symByLine.insertMulti(
					it.value()->astNode()->start->line,
					it.value());
		}
		for (ASTSymbolHash::iterator it = scope->compoundTypes().begin();
				it != scope->compoundTypes().end(); ++it)
		{
			symByLine.insertMulti(
					it.value()->astNode()->start->line,
					it.value());
		}
		QList<quint64> keys = symByLine.keys();
		qSort(keys);
		for (int i = 0; i < keys.size(); ++i) {
			QList<const ASTSymbol*> syms = symByLine.values(keys[i]);
			for (int j = 0; j < syms.size(); ++j)
				debugmsg(
						QString("  Line %1: %2 (%3)")
						.arg(syms[j]->astNode()->start->line, 5)
						.arg(syms[j]->name(), -40)
						.arg(ASTSymbol::typeToString(syms[j]->type())));
		}
	}

	debugmsg(
			QString("================================="
					"============"
					"================================="));

}


quint32 AbstractSyntaxTree::errorCount() const
{
    return _psr ? _psr->pParser->rec->errorCount : 0;
}


QString AbstractSyntaxTree::antlrTokenToStr(const pANTLR3_COMMON_TOKEN tok) const
{
    if (!tok)
        return QString();
    if (!_antlrStringCache.contains(tok)) {
        pANTLR3_STRING s = tok->getText(tok);
        _antlrStringCache.insert(
                    tok, QString::fromAscii((const char*)s->chars, s->len));
    }
    return _antlrStringCache[tok];
}


QString AbstractSyntaxTree::antlrStringToStr(const pANTLR3_STRING s) const
{
    if (!s)
        return QString();
    if (!_antlrStringCache.contains(s)) {
        _antlrStringCache.insert(
                    s, QString::fromAscii((const char*)s->chars, s->len));
    }
    return _antlrStringCache[s];
}

